#include "stdafx.h"

#include "DetouredFunctions.hpp"

#include "AllEvents.h"
#include "PostOffice.h"
#include "WinsockHooks.h"

using namespace events;


HANDLE
WINAPI
HookCreateFileW(
    LPCWSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile
	)
#define PARAMS lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile
{
	HANDLE hRes = OriginalAPI::CreateFileW(PARAMS);
	ErrorCodeRestore erc;
 	if (StateIndicator::IsInternalState())
		return hRes;

	{
		StateIndicator::Setter isInternal;
		FileEvent *pEvent = new FileEvent(FileEvent::FE_OPEN);
		pEvent->setFileHandle(hRes);
		pEvent->setFileName(lpFileName == NULL ? L"" : lpFileName);
		PostOffice::GetInstance().send(pEvent);
	}

	return hRes;
}
#undef PARAMS


HANDLE
WINAPI
HookCreateFileA(
    LPCSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile
	)
#define PARAMS lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile
{
	HANDLE hRes = OriginalAPI::CreateFileA(PARAMS);
	ErrorCodeRestore erc;
	if (StateIndicator::IsInternalState())
		return hRes;

	{
		StateIndicator::Setter isInternal;
		FileEvent *pEvent = new FileEvent(FileEvent::FE_OPEN);
		pEvent->setFileHandle(hRes);
		pEvent->setFileName(std::string(lpFileName == NULL ? "" : lpFileName));
		PostOffice::GetInstance().send(pEvent);
	}

	return hRes;
}
#undef PARAMS


BOOL
WINAPI
HookWriteFile(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped
    )
#define PARAMS hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::WriteFile(PARAMS);

	{
		StateIndicator::Setter isInternal;
		FileEvent *pEvent = new FileEvent(FileEvent::FE_WRITE);
		pEvent->setFileHandle(hFile);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::WriteFile(PARAMS);
}
#undef PARAMS

BOOL
WINAPI
HookReadFile(
    HANDLE hFile,
    LPVOID lpBuffer,
    DWORD nNumberOfBytesToRead,
    LPDWORD lpNumberOfBytesRead,
    LPOVERLAPPED lpOverlapped
    )
#define PARAMS hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::ReadFile(PARAMS);

	{
		StateIndicator::Setter isInternal;
		FileEvent *pEvent = new FileEvent(FileEvent::FE_READ);
		pEvent->setFileHandle(hFile);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::ReadFile(PARAMS);
}
#undef PARAMS

LPVOID
WINAPI
HookVirtualAlloc(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect
    )
#define PARAMS lpAddress, dwSize, flAllocationType, flProtect
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::VirtualAlloc(PARAMS);

	{
		StateIndicator::Setter isInternal;
		MemoryEvent *pEvent = new MemoryEvent(flAllocationType, dwSize);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::VirtualAlloc(PARAMS);
}
#undef PARAMS

LPVOID
WINAPI
HookVirtualAllocEx(
    HANDLE hProcess,
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect
    )
#define PARAMS hProcess, lpAddress, dwSize, flAllocationType, flProtect
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::VirtualAllocEx(PARAMS);

	{
		StateIndicator::Setter isInternal;
		MemoryEvent *pEvent = new MemoryEvent(flAllocationType, dwSize);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::VirtualAllocEx(PARAMS);
}
#undef PARAMS

BOOL
WINAPI
HookDuplicateHandle(
    HANDLE hSourceProcessHandle,
    HANDLE hSourceHandle,
    HANDLE hTargetProcessHandle,
    LPHANDLE lpTargetHandle,
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwOptions
    )
#define PARAMS hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions
{
	BOOL bRes = OriginalAPI::DuplicateHandle(PARAMS);
	ErrorCodeRestore erc;
	if (StateIndicator::IsInternalState())
		return bRes;

	{
		StateIndicator::Setter isInternal;
		MiscEvents *pEvent = new MiscEvents(MiscEvents::MISC_DUPLICATE_HANDLE);
		pEvent->setDuplicateHandles(hSourceHandle, (bRes == FALSE ? ((HANDLE)0) : *lpTargetHandle));
		PostOffice::GetInstance().send(pEvent);
	}

	return bRes;
}
#undef PARAMS

void
WINAPI
HookExitProcess(
	UINT uExitCode
	)
{
	{
		StateIndicator::Setter isInternal;

		MiscEvents *pEvent = new MiscEvents(MiscEvents::MISC_EXIT_PROCESS);
		pEvent->setExitProcessCode(uExitCode);
		PostOffice::GetInstance().send(pEvent);

		PostOffice::GetInstance().stopLogger();
	}
	OriginalAPI::ExitProcess(uExitCode);
}


// stores original parameters for thread creation
struct create_thread_info {
	LPTHREAD_START_ROUTINE lpStartAddress;
	LPVOID lpParameter;
};

// When a new thread is created, we want to be notified when
// the thread exits. For this purpose we'll replace original
// start routine with this function, and will call the original
// from here.
// When the original function returns, we wil know the thread
// is terminating.
DWORD WINAPI HookThreadRoutine(LPVOID lpParam) {
	struct create_thread_info *pInfo = reinterpret_cast<struct create_thread_info*>(lpParam);
	DWORD res = pInfo->lpStartAddress(pInfo->lpParameter);

	StateIndicator::Setter isInternal;
	ThreadEvent *pEvent = new ThreadEvent(ThreadEvent::THREAD_EV_FINISH, ::GetCurrentThreadId());
	PostOffice::GetInstance().send(pEvent);

	delete pInfo;
	return res;
}

HANDLE
WINAPI
HookCreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    SIZE_T dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
    )
#define PARAMS lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId
{
	if (StateIndicator::IsInternalState() || lpStartAddress == NULL)
		return OriginalAPI::CreateThread(PARAMS);

	struct create_thread_info *pInfo = NULL;
	{
		StateIndicator::Setter isInternal;
		// prepare info for new thread
		// (will be deleted in new thread)
		pInfo = new struct create_thread_info();
		if (pInfo != NULL) {
			pInfo->lpStartAddress = lpStartAddress;
			pInfo->lpParameter = lpParameter;
		}
	}
	if (!pInfo)
		return OriginalAPI::CreateThread(PARAMS);

	// execute original CreateThread
	// but using a different function
	HANDLE hRes = OriginalAPI::CreateThread(lpThreadAttributes, dwStackSize, HookThreadRoutine, pInfo, dwCreationFlags, lpThreadId);
	ErrorCodeRestore erc;

	// send event
	{
		StateIndicator::Setter isInternal;
		ThreadEvent *pEvent = new ThreadEvent(ThreadEvent::THREAD_EV_CREATE, hRes);
		PostOffice::GetInstance().send(pEvent);
	}

	return hRes;
}
#undef PARAMS


VOID
WINAPI
HookExitThread(
    DWORD dwExitCode
	)
{
	if (StateIndicator::IsInternalState())
		OriginalAPI::ExitThread(dwExitCode);

	{
		StateIndicator::Setter isInternal;
		ThreadEvent *pEvent = new ThreadEvent(ThreadEvent::THREAD_EV_EXIT, ::GetCurrentThreadId());
		PostOffice::GetInstance().send(pEvent);
	}

	OriginalAPI::ExitThread(dwExitCode);
}

BOOL
WINAPI
HookTerminateThread(
    HANDLE hThread,
    DWORD dwExitCode
    )
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::TerminateThread(hThread, dwExitCode);

	{
		StateIndicator::Setter isInternal;
		ThreadEvent *pEvent = new ThreadEvent(ThreadEvent::THREAD_EV_TERMINATE, hThread);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::TerminateThread(hThread, dwExitCode);
}


HWND
WINAPI
HookCreateWindowExA(
    DWORD dwExStyle,
    LPCSTR lpClassName,
    LPCSTR lpWindowName,
    DWORD dwStyle,
    int X,
    int Y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpParam)
#define PARAMS dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::CreateWindowExA(PARAMS);

	{
		StateIndicator::Setter isInternal;
		try {
			WindowEvent *pEvent = new events::WindowEvent(std::string((((unsigned long)lpClassName) & 0xffff0000) == 0 ? "" : lpClassName), std::string(lpWindowName == NULL ? "" : lpWindowName), dwStyle);
			PostOffice::GetInstance().send(pEvent);
		} catch(...) {}
	}

	return OriginalAPI::CreateWindowExA(PARAMS);
}
// same params are used for "W" version

HWND
WINAPI
HookCreateWindowExW(
    DWORD dwExStyle,
    LPCWSTR lpClassName,
    LPCWSTR lpWindowName,
    DWORD dwStyle,
    int X,
    int Y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpParam) 
// same params as in "A" version
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::CreateWindowExW(PARAMS);

	{
		StateIndicator::Setter isInternal;
		try {
			WindowEvent *pEvent = new events::WindowEvent(((((unsigned long)lpClassName) & 0xffff0000) == 0 ? L"" : lpClassName), (lpWindowName == NULL ? L"" : lpWindowName), dwStyle);
			PostOffice::GetInstance().send(pEvent);
		} catch(...) {}
	}

	return OriginalAPI::CreateWindowExW(PARAMS);
}
#undef PARAMS

// We want ot notify "WinsockHooks" class each time a new
// library is loaded
HMODULE
WINAPI
HookLoadLibraryA(
    LPCSTR lpLibFileName
    )
{
	HMODULE hres = OriginalAPI::LoadLibraryA(lpLibFileName);
	ErrorCodeRestore erc;
	if (StateIndicator::IsInternalState())
		return hres;

	{
		StateIndicator::Setter isInternal;
		if (hres != NULL && lpLibFileName != NULL)
			WinsockHooks::GetInstance().notifyDLLWasLoaded(std::string(lpLibFileName), hres);
	}

	return hres;
}

HMODULE
WINAPI
HookLoadLibraryW(
    LPCWSTR lpLibFileName
    )
{
	HMODULE hres = OriginalAPI::LoadLibraryW(lpLibFileName);
	ErrorCodeRestore erc;
	if (StateIndicator::IsInternalState())
		return hres;

	{
		StateIndicator::Setter isInternal;
		if (hres != NULL && lpLibFileName != NULL)
			WinsockHooks::GetInstance().notifyDLLWasLoaded(lpLibFileName, hres);
	}

	return hres;
}

HMODULE
WINAPI
HookLoadLibraryExA(
    LPCSTR lpLibFileName,
    HANDLE hFile,
    DWORD dwFlags
    )
{
	HMODULE hres = OriginalAPI::LoadLibraryExA(lpLibFileName, hFile, dwFlags);
	ErrorCodeRestore erc;
	if (StateIndicator::IsInternalState())
		return hres;

	{
		StateIndicator::Setter isInternal;
		if (hres != NULL && lpLibFileName != NULL)
			WinsockHooks::GetInstance().notifyDLLWasLoaded(std::string(lpLibFileName), hres);
	}

	return hres;
}

HMODULE
WINAPI
HookLoadLibraryExW(
    LPCWSTR lpLibFileName,
    HANDLE hFile,
    DWORD dwFlags
    )
{
	HMODULE hres = OriginalAPI::LoadLibraryExW(lpLibFileName, hFile, dwFlags);
	ErrorCodeRestore erc;
	if (StateIndicator::IsInternalState())
		return hres;

	{
		StateIndicator::Setter isInternal;
		if (hres != NULL && lpLibFileName != NULL)
			WinsockHooks::GetInstance().notifyDLLWasLoaded(lpLibFileName, hres);
	}

	return hres;
}


DWORD
WINAPI
HookWaitForSingleObject(
    HANDLE hHandle,
    DWORD dwMilliseconds
    )
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::WaitForSingleObject(hHandle, dwMilliseconds);

	{
		StateIndicator::Setter isInternal;
		SyncEvent *pEvent = new SyncEvent(SyncEvent::SYNC_WAIT_SINGLE, 1);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::WaitForSingleObject(hHandle, dwMilliseconds);
}

DWORD
WINAPI
HookWaitForMultipleObjects(
    DWORD nCount,
    CONST HANDLE *lpHandles,
    BOOL bWaitAll,
    DWORD dwMilliseconds
    )
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::WaitForMultipleObjects(nCount, lpHandles, bWaitAll, dwMilliseconds);

	{
		StateIndicator::Setter isInternal;
		SyncEvent *pEvent = new SyncEvent(SyncEvent::SYNC_WAIT_MULTIPLE, nCount);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::WaitForMultipleObjects(nCount, lpHandles, bWaitAll, dwMilliseconds);
}


DWORD
WINAPI
HookWaitForSingleObjectEx(
    HANDLE hHandle,
    DWORD dwMilliseconds,
    BOOL bAlertable
    )
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::WaitForSingleObjectEx(hHandle, dwMilliseconds, bAlertable);

	{
		StateIndicator::Setter isInternal;
		SyncEvent *pEvent = new SyncEvent(SyncEvent::SYNC_WAIT_SINGLE_EX, 1);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::WaitForSingleObjectEx(hHandle, dwMilliseconds, bAlertable);
}


DWORD
WINAPI
HookWaitForMultipleObjectsEx(
    DWORD nCount,
    CONST HANDLE *lpHandles,
    BOOL bWaitAll,
    DWORD dwMilliseconds,
    BOOL bAlertable
    )
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::WaitForMultipleObjectsEx(nCount, lpHandles, bWaitAll, dwMilliseconds, bAlertable);

	{
		StateIndicator::Setter isInternal;
		SyncEvent *pEvent = new SyncEvent(SyncEvent::SYNC_WAIT_MULTIPLE_EX, nCount);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::WaitForMultipleObjectsEx(nCount, lpHandles, bWaitAll, dwMilliseconds, bAlertable);
}


DWORD
WINAPI
HookMsgWaitForMultipleObjects(
    DWORD nCount,
    CONST HANDLE *pHandles,
    BOOL fWaitAll,
    DWORD dwMilliseconds,
    DWORD dwWakeMask)
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll, dwMilliseconds, dwWakeMask);

	{
		StateIndicator::Setter isInternal;
		SyncEvent *pEvent = new SyncEvent(SyncEvent::SYNC_MSG_WAIT, nCount);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll, dwMilliseconds, dwWakeMask);
}


DWORD
WINAPI
HookMsgWaitForMultipleObjectsEx(
    DWORD nCount,
    CONST HANDLE *pHandles,
    DWORD dwMilliseconds,
    DWORD dwWakeMask,
    DWORD dwFlags)
{
	if (StateIndicator::IsInternalState())
		return OriginalAPI::MsgWaitForMultipleObjectsEx(nCount, pHandles, dwMilliseconds, dwWakeMask, dwFlags);

	{
		StateIndicator::Setter isInternal;
		SyncEvent *pEvent = new SyncEvent(SyncEvent::SYNC_MSG_WAIT_EX, nCount);
		PostOffice::GetInstance().send(pEvent);
	}

	return OriginalAPI::MsgWaitForMultipleObjectsEx(nCount, pHandles, dwMilliseconds, dwWakeMask, dwFlags);
}

